package com.song.mybatis.plugin;

import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.exception.ShellException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

public class MapperPlugin extends PluginAdapter {

    private static final String DEFAULT_DAO_SUPER_CLASS = "com.song.base.dao.BaseMapper";
    private static final String DEFAULT_EXPAND_DAO_SUPER_CLASS = "com.song.base.BaseExpandMapper";
    private String daoTargetDir;
    private String daoTargetPackage;

    private String daoSuperClass;

    // 扩展
    private String expandDaoTargetPackage;
    private String expandDaoSuperClass;

    private ShellCallback shellCallback = null;

    public MapperPlugin() {
        shellCallback = new DefaultShellCallback(false);
    }

    /**
     * 验证参数是否有效
     * @param warnings
     * @return
     */
    public boolean validate(List<String> warnings) {
        daoTargetDir = properties.getProperty("targetProject");
        boolean valid = stringHasValue(daoTargetDir);

        daoTargetPackage = properties.getProperty("targetPackage");
        boolean valid2 = stringHasValue(daoTargetPackage);

        daoSuperClass = properties.getProperty("daoSuperClass");
        if (!stringHasValue(daoSuperClass)) {
            daoSuperClass = DEFAULT_DAO_SUPER_CLASS;
        }

        expandDaoTargetPackage = properties.getProperty("expandTargetPackage");
        expandDaoSuperClass = properties.getProperty("expandDaoSuperClass");
        if (!stringHasValue(expandDaoSuperClass)) {
            expandDaoSuperClass = DEFAULT_EXPAND_DAO_SUPER_CLASS;
        }
        return valid && valid2;
    }

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {

        XmlElement parentElement = document.getRootElement();

        XmlElement baseWhere = createBaseWhere(introspectedTable);
        parentElement.addElement(baseWhere);

        XmlElement count = createCount(introspectedTable);
        parentElement.addElement(count);

        XmlElement findByMap = createFindByMap(introspectedTable);
        parentElement.addElement(findByMap);

        return super.sqlMapDocumentGenerated(document, introspectedTable);
    }

    /**
     * 创建baseWhere
     * @param introspectedTable
     * @return
     */
    private XmlElement createBaseWhere(IntrospectedTable introspectedTable) {
        XmlElement base = new XmlElement("sql");
        base.addAttribute(new Attribute("id","Base_Where"));
        XmlElement baseWhere = new XmlElement("where");
        base.addElement(baseWhere);
        for(IntrospectedColumn column : introspectedTable.getAllColumns()){
            XmlElement columnIf = new XmlElement("if");
            columnIf.addAttribute(new Attribute("test",column.getJavaProperty() + " != null"));
            columnIf.addElement(new TextElement(" AND `"+column.getActualColumnName()+"` = #{" + column.getJavaProperty() + "}"));
            baseWhere.addElement(columnIf);
        }
        return base;
    }
    /**
     * 创建查询计数
     * @param introspectedTable
     * @return
     */
    private XmlElement createCount(IntrospectedTable introspectedTable) {
        XmlElement select = new XmlElement("select");
        select.addAttribute(new Attribute("id", "count"));
        select.addAttribute(new Attribute("resultType", "java.lang.Integer"));
        select.addAttribute(new Attribute("parameterType", "java.util.Map"));
        select.addElement(new TextElement(" select count(*) from "+ introspectedTable.getFullyQualifiedTableNameAtRuntime() ));
        XmlElement include = new XmlElement("include");
        include.addAttribute(new Attribute("refid","Base_Where"));
        select.addElement(include);
        return select;
    }
    /**
     * 创建查询
     * @param introspectedTable
     * @return
     */
    private XmlElement createFindByMap(IntrospectedTable introspectedTable) {
        XmlElement select = new XmlElement("select");
        select.addAttribute(new Attribute("id", "findByMap"));
        select.addAttribute(new Attribute("resultMap", "BaseResultMap"));
        select.addAttribute(new Attribute("parameterType", "java.util.Map"));
        select.addElement(new TextElement(" select "));
        XmlElement includeColumn = new XmlElement("include");
        includeColumn.addAttribute(new Attribute("refid","Base_Column_List"));
        select.addElement(includeColumn);
        select.addElement(new TextElement(" from "+ introspectedTable.getFullyQualifiedTableNameAtRuntime() ));
        XmlElement include = new XmlElement("include");
        include.addAttribute(new Attribute("refid","Base_Where"));
        select.addElement(include);

        XmlElement limit = new XmlElement("if");
        limit.addAttribute(new Attribute("test","pageOffset != null and pageLimit != null"));
        limit.addElement(new TextElement(" LIMIT #{pageOffset},#{pageLimit} "));
        select.addElement(limit);

        return select;
    }

    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        JavaFormatter javaFormatter = context.getJavaFormatter();
        List<GeneratedJavaFile> mapperJavaFiles = new ArrayList<GeneratedJavaFile>();
        for (GeneratedJavaFile javaFile : introspectedTable.getGeneratedJavaFiles()) {
            CompilationUnit unit = javaFile.getCompilationUnit();
            FullyQualifiedJavaType baseModelJavaType = unit.getType();

            String shortName = baseModelJavaType.getShortName();
            GeneratedJavaFile mapperJavafile = null;

            if (shortName.endsWith("Mapper")) { // 扩展Mapper
                if (stringHasValue(expandDaoTargetPackage)) {
                    Interface mapperInterface = new Interface(
                            expandDaoTargetPackage + "." + shortName.replace("Mapper", "ExpandMapper"));
                    mapperInterface.setVisibility(JavaVisibility.PUBLIC);
                    mapperInterface.addJavaDocLine("/**");
                    mapperInterface.addJavaDocLine(" * " + shortName + "扩展");
                    mapperInterface.addJavaDocLine(" */");

                    FullyQualifiedJavaType daoSuperType = new FullyQualifiedJavaType(expandDaoSuperClass);
                    mapperInterface.addImportedType(daoSuperType);
                    mapperInterface.addSuperInterface(daoSuperType);

                    mapperJavafile = new GeneratedJavaFile(mapperInterface, daoTargetDir, javaFormatter);
                    try {
                        File mapperDir = shellCallback.getDirectory(daoTargetDir, daoTargetPackage);
                        File mapperFile = new File(mapperDir, mapperJavafile.getFileName());
                        // 文件不存在
                        if (!mapperFile.exists()) {
                            mapperJavaFiles.add(mapperJavafile);
                        }
                    } catch (ShellException e) {
                        e.printStackTrace();
                    }
                }
            } else if (!shortName.endsWith("Example")) { // CRUD Mapper
                Interface mapperInterface = new Interface(daoTargetPackage + "." + shortName + "Mapper");

                mapperInterface.setVisibility(JavaVisibility.PUBLIC);
                mapperInterface.addJavaDocLine("/**");
                mapperInterface.addJavaDocLine(" * 由MyBatis Generator工具自动生成，请不要手动修改");
                mapperInterface.addJavaDocLine(" */");

                FullyQualifiedJavaType daoSuperType = new FullyQualifiedJavaType(daoSuperClass);
                // 添加泛型支持
                daoSuperType.addTypeArgument(baseModelJavaType);
                mapperInterface.addImportedType(baseModelJavaType);
                mapperInterface.addImportedType(daoSuperType);
                mapperInterface.addSuperInterface(daoSuperType);
                List<Method> methods = mapperInterface.getMethods();
                methods.clear();
                mapperJavafile = new GeneratedJavaFile(mapperInterface, daoTargetDir, javaFormatter);
                mapperJavaFiles.add(mapperJavafile);

            }
        }
        return mapperJavaFiles;
    }
}